/*
# Copyright (c) 2024 Huawei Device Co., Ltd.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
*/

#include "ohos_video_receiver.h"
#include "pj/log.h"
#include <multimedia/image_framework/image/image_receiver_native.h>
#include <ohcamera/camera.h>
#include <ohcamera/camera_manager.h>
#include <string>
#include <hilog/log.h>

#define THIS_FILE "ohos_video_receiver.cpp"
std::map<OH_ImageReceiverNative *, VideoFrameReceiver *> VideoFrameReceiver::receiverMap_;

VideoFrameReceiver *VideoFrameReceiver::Create(int32_t width, int32_t height, FrameCallBack observer) {
    return new VideoFrameReceiver(width, height, observer);
}


VideoFrameReceiver::VideoFrameReceiver(int32_t width, int32_t height, FrameCallBack observer)
    : width_(width), height_(height), callback_(observer) {
    InitCamera(width_, height_);
}
VideoFrameReceiver::~VideoFrameReceiver() {
    PJ_LOG(4, (THIS_FILE, "VideoFrameReceiver destory"));
    destroy();
}

void VideoFrameReceiver::OnImageReceiverCallback_1(OH_ImageReceiverNative *receiver) {

    auto it = receiverMap_.find(receiver);
    if (it == receiverMap_.end()) {
        return;
    }
    it->second->OnImageReceiverCallback();
}


void VideoFrameReceiver::OnImageReceiverCallback() {
    OH_ImageNative *image;
    Image_ErrorCode ret = OH_ImageReceiverNative_ReadLatestImage(receiver_, &image);
    if (ret != IMAGE_SUCCESS) {
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_DOMAIN, THIS_FILE, "%{public}s:%{public}d ret:%{public}d",  __func__, __LINE__, ret);
        return;
    }
    Image_Size imageSize;
    ret = OH_ImageNative_GetImageSize(image, &imageSize);
    if (ret != IMAGE_SUCCESS) {
        OH_ImageNative_Release(image);
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_DOMAIN, THIS_FILE, "%{public}s:%{public}d ret:%{public}d",  __func__, __LINE__, ret);
        return;
    }

    size_t typeSize;
     ret = OH_ImageNative_GetComponentTypes(image, nullptr, &typeSize);
    if (ret != IMAGE_SUCCESS || typeSize <= 0) {
        OH_ImageNative_Release(image);
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_DOMAIN, THIS_FILE, "%{public}s:%{public}d ret:%{public}d",  __func__, __LINE__, ret);
        return;
    }
    uint32_t *types = new uint32_t[typeSize];
    ret = OH_ImageNative_GetComponentTypes(image, &types, &typeSize);

    if (ret != IMAGE_SUCCESS) {
        OH_ImageNative_Release(image);
        delete[] types;
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_DOMAIN, THIS_FILE, "%{public}s:%{public}d ret:%{public}d",  __func__, __LINE__, ret);
        return;
    }

    int32_t rowStride;
    ret = OH_ImageNative_GetRowStride(image, types[0], &rowStride);
    if (ret != IMAGE_SUCCESS) {
        OH_ImageNative_Release(image);
        delete[] types;
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_DOMAIN, THIS_FILE, "%{public}s:%{public}d ret:%{public}d",  __func__, __LINE__, ret);
        return;
    }

    int32_t pixelStride;
    ret = OH_ImageNative_GetPixelStride(image, types[0], &pixelStride);
    if (ret != IMAGE_SUCCESS) {
        OH_ImageNative_Release(image);
        delete[] types;
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_DOMAIN, THIS_FILE, "%{public}s:%{public}d ret:%{public}d",  __func__, __LINE__, ret);
        return;
    }
    OH_NativeBuffer *buffer;
    size_t bufferSize;
    ret = OH_ImageNative_GetByteBuffer(image, types[0], &buffer);
    if (ret != IMAGE_SUCCESS) {

        OH_ImageNative_Release(image);
        delete[] types;
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_DOMAIN, THIS_FILE, "%{public}s:%{public}d ret:%{public}d",  __func__, __LINE__, ret);
        return;
    }

    ret = OH_ImageNative_GetBufferSize(image, types[0], &bufferSize);
    if (ret != IMAGE_SUCCESS) {
        OH_ImageNative_Release(image);
        delete[] types;
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_DOMAIN, THIS_FILE, "%{public}s:%{public}d ret:%{public}d",  __func__, __LINE__, ret);
        return;
    }

    delete[] types;
    uint8_t *srcBuffer = nullptr;
    int32_t retInt = OH_NativeBuffer_Map(buffer, (void **)&srcBuffer);
    if (retInt != 0) {
        OH_ImageNative_Release(image);
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_DOMAIN, THIS_FILE, "%{public}s:%{public}d ret:%{public}d",  __func__, __LINE__, retInt);
        return;
    }
    if (rowStride != imageSize.width) {
         // 传给其他支持stride的接口处理，或去除stride数据
        // 去除stride数据示例:将byteBuffer中的数据去除stride，拷贝得到新的dstBuffer数据
        size_t dstBufferSize = imageSize.width * imageSize.height * 1.5; // 相机预览流返回NV21格式
        std::unique_ptr<uint8_t[]> dstBuffer = std::make_unique<uint8_t[]>(dstBufferSize);
        uint8_t *dstPtr = dstBuffer.get();
        for (int j = 0; j < imageSize.height * 1.5; j++) {
            memcpy(dstPtr, srcBuffer, imageSize.width);
            dstPtr += imageSize.width;
            srcBuffer += rowStride;
        }
    } 
    callback_.onFrameCallBack(steam, srcBuffer, bufferSize);
    OH_NativeBuffer_Unmap(buffer); // 释放buffer,保证bufferQueue正常轮转
    OH_ImageNative_Release(image);
}



void VideoFrameReceiver::CreateVideoFrameReceiver() {
    PJ_LOG(4, (THIS_FILE, "CreateVideoFrameReceiver surfaceId:%lu", surfaceId_));
    OH_ImageReceiverOptions *options = nullptr;
    OH_ImageReceiverNative *imageReceiver = nullptr;
    int32_t capacity = 8; // BufferQueue里最大Image数量，推荐填写8
    Image_ErrorCode errCode = OH_ImageReceiverOptions_Create(&options);
    if (errCode != IMAGE_SUCCESS) {
        PJ_LOG(3, (THIS_FILE, "OH_ImageReceiverOptions_Create fail,errorCode:%d", errCode));
        return;
    }
    errCode = OH_ImageReceiverOptions_SetSize(options, {static_cast<uint32_t>(width_), static_cast<uint32_t>(height_)});
    if (errCode != IMAGE_SUCCESS) {
        PJ_LOG(3, (THIS_FILE, "OH_ImageReceiverOptions_SetSize fail,errorCode:%d", errCode));
        return;
    }
    errCode = OH_ImageReceiverOptions_SetCapacity(options, capacity);
    if (errCode != IMAGE_SUCCESS) {
        PJ_LOG(3, (THIS_FILE, "OH_ImageReceiverOptions_SetCapacity fail,errorCode:%d", errCode));
        return;
    }
    errCode = OH_ImageReceiverNative_Create(options, &imageReceiver);
    if (errCode != IMAGE_SUCCESS) {
        PJ_LOG(3, (THIS_FILE, "OH_ImageReceiverNative_Create fail,errorCode:%d", errCode));
        return;
    }

    errCode = OH_ImageReceiverNative_GetReceivingSurfaceId(imageReceiver, &surfaceId_);
    if (errCode != IMAGE_SUCCESS) {
        PJ_LOG(3,
               (THIS_FILE, "OH_ImageReceiverNative_GetReceivingSurfaceId fail,imageReceiverID_:%d", errCode));
        return;
    }
    receiverMap_[imageReceiver] = this;
    receiver_ = imageReceiver;
    errCode = OH_ImageReceiverNative_On(imageReceiver, VideoFrameReceiver::OnImageReceiverCallback_1);
    if (errCode != IMAGE_SUCCESS) {
        PJ_LOG(3, (THIS_FILE, "OH_ImageReceiverNative_On fail,errorCode:%d", errCode));
        return;
    }
    errCode =  OH_ImageReceiverOptions_Release(options);
    if (errCode != IMAGE_SUCCESS) {
        PJ_LOG(3, (THIS_FILE, "OH_ImageReceiverOptions_Release fail,errorCode:%d", errCode));
        return;
    }
}


void VideoFrameReceiver::InitCamera(int32_t width, int32_t height) {
    PJ_LOG(4, (THIS_FILE, "InitCamera width:%d,height:%d", width, height));
    CreateVideoFrameReceiver();
    Camera_ErrorCode ret = OH_Camera_GetCameraManager(&camera_manager_);
    if (camera_manager_ == nullptr || ret != CAMERA_OK) {
        PJ_LOG(3, (THIS_FILE, "OH_Camera_GetCameraManager get fail,errorCode:%d", ret));
        return;
    }
    ret = OH_CameraManager_GetSupportedCameras(camera_manager_, &cameras_, &cameras_size_);
    if (cameras_ == nullptr || ret != CAMERA_OK || cameras_size_ <= 0) {
        PJ_LOG(3, (THIS_FILE, "OH_CameraManager_GetSupportedCameras get fail,errorCode:%d", ret));
        return;
    }
    PJ_LOG(4, (THIS_FILE, "OH_CameraManager_GetSupportedCameras cameras_size:%d", cameras_size_));
}

int VideoFrameReceiver::getCameraCount() {
    PJ_LOG(4, (THIS_FILE, "getCameraCount cameras size:%d", cameras_size_));
    return cameras_size_;
}
int VideoFrameReceiver::StartCamera() {
    PJ_LOG(4, (THIS_FILE, "StartCamera start,is_camera_started:%d", is_camera_started_));
    if (is_camera_started_) {
        // 重置相机
        StopCamera();
    }

    if (camera_input_ == nullptr) {
        CameraInputCreate();
    }
    if (preview_output_ == nullptr) {
        PreviewOutputCreate();
    }
    if (capture_session_ == nullptr) {
        CaptureSessionSetting();
    }
    Camera_ErrorCode ret = OH_CaptureSession_Start(capture_session_);
    if (ret != CAMERA_OK) {
        return ret;
    }
    PJ_LOG(4, (THIS_FILE, "OH_CaptureSession_Start success"));
    is_camera_started_ = true;
    return ret;
}

void VideoFrameReceiver::StopCamera() {
    PJ_LOG(4, (THIS_FILE, "StopCamera start"));
    if(capture_session_){
        OH_CaptureSession_Stop(capture_session_);
    }
}

void VideoFrameReceiver::destroy() {
    PJ_LOG(4, (THIS_FILE, "Camera destroy start"));
    StopCamera();
    if (camera_input_ != nullptr) 
    {
        OH_CameraInput_Close(camera_input_);
        OH_CameraInput_Release(camera_input_);
    }
    if (preview_output_ != nullptr) 
    {
        OH_PreviewOutput_Stop(preview_output_);
        OH_PreviewOutput_Release(preview_output_);
    }
    if (receiver_) {
        OH_ImageReceiverNative_Off(receiver_);
        OH_ImageReceiverNative_Release(receiver_);
        receiverMap_.erase(receiver_);
    }
    if (preview_profile_)
    {
       OH_PreviewOutput_DeleteProfile(preview_profile_);
    }
    if (capture_session_)
    {
       OH_CaptureSession_Release(capture_session_);
    }
     if (camera_manager_) {
        OH_Camera_DeleteCameraManager(camera_manager_);
    }
    steam = nullptr;
    preview_output_ = nullptr;
    camera_input_ = nullptr;
    capture_session_ = nullptr;
    is_camera_started_ = false;
    preview_profile_ = nullptr;
    camera_manager_ = nullptr;
    cameras_ = nullptr;
    receiver_ = nullptr;
}

Camera_Device VideoFrameReceiver::getCamera(int index) {
    if (index < 0) {
        index = camera_dev_index_;
    }
    return cameras_[index];
}
Camera_Profile** VideoFrameReceiver::getPreviewProfileByIndex(int index,uint32_t *size) {
    Camera_OutputCapability *cameraOutputCapability = nullptr;
    Camera_ErrorCode ret =
        OH_CameraManager_GetSupportedCameraOutputCapability(camera_manager_, &cameras_[index], &cameraOutputCapability);
    if (cameraOutputCapability == nullptr || ret != CAMERA_OK) {
        PJ_LOG(3,
               (THIS_FILE,
                "getPreviewProfileByIndex OH_CameraManager_GetSupportedCameraOutputCapability fail,errorCode:%d", ret));
  	return nullptr;
    }
    PJ_LOG(4, (THIS_FILE, "getPreviewProfileByIndex PreviewOutputCreate metadataProfilesSize:%d",
               cameraOutputCapability->metadataProfilesSize));
    if (cameraOutputCapability->metadataProfilesSize < 0) {
  	return nullptr;
    }
    PJ_LOG(4, (THIS_FILE, "getPreviewProfileByIndex PreviewOutputCreate previewProfilesSize:%d",
               cameraOutputCapability->previewProfilesSize));
   int count = 0;
    for (int i = 0; i < cameraOutputCapability->previewProfilesSize; i++) {
        preview_profile_ = cameraOutputCapability->previewProfiles[i];
        if (preview_profile_->size.width == width_ && preview_profile_->size.height == height_) {
            profile_index_ = i;
        }
        count++;
    }
    if (size) {
        *size = count;
    }
    PJ_LOG(4, (THIS_FILE, "profile_index_:%d",profile_index_));
    preview_profile_ = cameraOutputCapability->previewProfiles[profile_index_];
   return  cameraOutputCapability->previewProfiles;
}
void VideoFrameReceiver::PreviewOutputCreate() {
    getPreviewProfileByIndex(camera_dev_index_,nullptr);
    auto surfaceId = std::to_string(surfaceId_);
    PJ_LOG(4, (THIS_FILE, "PreviewOutputSteamCreate start"));
    Camera_ErrorCode ret =
        OH_CameraManager_CreatePreviewOutput(camera_manager_, preview_profile_, surfaceId.c_str(), &preview_output_);
    if (preview_profile_ == nullptr || preview_output_ == nullptr || ret != CAMERA_OK) {
        PJ_LOG(3, (THIS_FILE, "OH_CameraManager_CreatePreviewOutput fail,errorCode:%d", ret));
    }
}

void VideoFrameReceiver::CameraInputCreate() {
    PJ_LOG(4, (THIS_FILE, "CameraInputCreate start"));
    Camera_ErrorCode ret =
        OH_CameraManager_CreateCameraInput(camera_manager_, &cameras_[camera_dev_index_], &camera_input_);
    if (camera_input_ == nullptr || ret != CAMERA_OK) {
        PJ_LOG(3, (THIS_FILE, "OH_CameraManager_CreateCameraInput fail,errorCode:%d", ret));
    }
    ret = OH_CameraInput_Open(camera_input_);
    if (ret != CAMERA_OK) {
        PJ_LOG(3, (THIS_FILE, "OH_CameraInput_Open fail,errorCode:%d", ret));
    }
}

void VideoFrameReceiver::CaptureSessionSetting() {
    Camera_ErrorCode ret;
    ret = OH_CameraManager_CreateCaptureSession(camera_manager_, &capture_session_);
    if (capture_session_ == nullptr || ret != CAMERA_OK) {
        return;
    }
    ret = OH_CaptureSession_SetSessionMode(capture_session_, NORMAL_VIDEO);
    if (ret != CAMERA_OK) {
    }
    ret = OH_CaptureSession_BeginConfig(capture_session_);
    if (ret != CAMERA_OK) {
    }
    if (camera_input_) {
        ret = OH_CaptureSession_AddInput(capture_session_, camera_input_);
        if (ret != CAMERA_OK) {
            return;
        }
    }
    if (preview_output_) {
        ret = OH_CaptureSession_AddPreviewOutput(capture_session_, preview_output_);
        if (ret != CAMERA_OK) {
            return;
        }
    }
    ret = OH_CaptureSession_CommitConfig(capture_session_);
    if (ret != CAMERA_OK) {
        return;
    }
}